﻿using CrystalDecisions.CrystalReports.Engine;
using CrystalDecisions.Shared;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace BLL.Utility
{
    public class CrystalReports : IDisposable
    {
        readonly SqlConnectionStringBuilder _sqlConnStringBuider = new SqlConnectionStringBuilder();
        readonly ConnectionInfo _connInfo = new ConnectionInfo();
        ReportDocument _reportDoc = new ReportDocument();
        FileInfo _fileInfo;

        #region -- Construtores --
        /// <summary>
        /// Construtor.
        /// </summary>
        public CrystalReports()
        {
            SetConnection();
        }
        #endregion

        #region -- Métodos Privados --
        /// <summary>
        /// Configura a conexão com o banco de dados.
        /// </summary>    
        private void SetConnection()
        {
            var sb = new SqlConnectionStringBuilder(ConfiguracaoWeb.StringConexao());
            _sqlConnStringBuider.ConnectionString = sb.ConnectionString;
            _connInfo.UserID = _sqlConnStringBuider.UserID;
            _connInfo.Password = _sqlConnStringBuider.Password;
            _connInfo.DatabaseName = _sqlConnStringBuider.InitialCatalog;
            _connInfo.ServerName = _sqlConnStringBuider.DataSource;
            _connInfo.Type = ConnectionInfoType.SQL;
        }

        /// <summary>
        /// Preenche as informações do relatório.
        /// </summary>
        /// <param name="filename">Nome/Caminho do arquivo .rpt do relatório.</param>
        /// <returns>Retorna o objeto/relatório.</returns>
        private ReportDocument SetReport(string filename)
        {
            _fileInfo = new FileInfo(filename);
            _reportDoc.Load(filename);

            // Conexão inicial
            _reportDoc.SetDatabaseLogon(_sqlConnStringBuider.UserID, _sqlConnStringBuider.Password, 
                _sqlConnStringBuider.InitialCatalog, _sqlConnStringBuider.DataSource);
            var reportTables = _reportDoc.Database.Tables;

            //Percorre todas as tabelas do relatório para verificar se o Databease é igual a da string de conexão
            foreach (Table table in reportTables)
            {
                _reportDoc.SetDatabaseLogon(_connInfo.UserID, _connInfo.Password, _connInfo.DatabaseName, _connInfo.ServerName);
                table.LogOnInfo.ConnectionInfo.AllowCustomConnection = true;
                table.LogOnInfo.ConnectionInfo = _connInfo;
                table.ApplyLogOnInfo(table.LogOnInfo);
            }

            foreach (var subReport in (from Section section in _reportDoc.ReportDefinition.Sections from ReportObject reportObject in section.ReportObjects where reportObject.Kind == ReportObjectKind.SubreportObject select reportObject).Cast<SubreportObject>())
            {
                using (var subDocument = subReport.OpenSubreport(subReport.SubreportName))
                {
                    foreach (Table table in subDocument.Database.Tables)
                    {
                        table.LogOnInfo.ConnectionInfo.AllowCustomConnection = true;
                        var logOnInfo = table.LogOnInfo;
                        logOnInfo.ConnectionInfo = _connInfo;
                        table.ApplyLogOnInfo(logOnInfo);
                    }
                }
            }

            return _reportDoc;

        }

        /// <summary>
        /// Retorna o nome do relatório que será exportado.
        /// </summary>
        private string ExportingName
        {
            get
            {
                return _fileInfo.Name.Replace(_fileInfo.Extension, "");
            }
        }

        /// <summary>
        /// Exporta para o tipo de formato escolhido.
        /// </summary>
        /// <param name="type">Tipo do formato.</param>
        private void ExportTo(ExportFormatType type, string contentType)
        {
            HttpContext.Current.Response.Clear();
            HttpContext.Current.Response.ClearHeaders();
            HttpContext.Current.Response.ClearContent();
            HttpContext.Current.Response.ContentType = contentType;
            HttpContext.Current.Response.BufferOutput = true;
            _reportDoc.ExportToHttpResponse(type, HttpContext.Current.Response, false, ExportingName);
        }
        #endregion

        #region -- Métodos Públicos --
        /// <summary>
        /// Preenche os parâmetros do relatório.
        /// </summary>
        /// <param name="name">Nome do parâmentro.</param>
        /// <param name="valor">Valor do parâmentro.</param>
        public void SetParameter(string name, object valor)
        {
            var myValue = new ParameterDiscreteValue();
            var myValues = _reportDoc.DataDefinition.ParameterFields[name].CurrentValues;
            myValue.Value = valor;
            myValues.Add(myValue);
            _reportDoc.DataDefinition.ParameterFields[name].ApplyCurrentValues(myValues);
        }

        /// <summary>
        /// Verifica todos os parâmentros necessários para o relatório.
        /// </summary>
        /// <param name="pQtdParametros"></param>
        /// <returns>Retorna verdadeiro se todos os parâmetros foram informados.</returns>
        public bool ReportParametersIsValid(int pQtdParametros)
        {
            // Se o relatório principal tiver sub-relatórios, deve-se capturar a quantidade de parâmetros dos mesmos.
            var pQtdParametrosSubs = 0;

            // Percorre todos os parâmetros do relatório principal e dos sub-relatórios.
            for (var i = 0; i < _reportDoc.ParameterFields.Count; i++)
            {
                // Obs1: Todos os parâmetros do RELATÓRIO PRINCIPAL NÃO PRECISAM especificar o nome de seu relatório.
                // Obs2: Todos os parâmetros dos SUB-RELATÓRIOS PRECISAM especificar o nome de seu relatório.
                // Se o nome do relatório do parâmetro "[i]" for diferente de vazio, soma-se um a quantidade.
                if (!_reportDoc.ParameterFields[i].ReportName.Equals(string.Empty))
                    pQtdParametrosSubs += 1;
            }

            // A soma dos parâmetros do relatório principal (pQtdParametros) e de seus sub-relatórios (pQtdParametrosSubs), deve ser igual a quantidade total dos parâmetros.
            return (pQtdParametros + pQtdParametrosSubs) == (_reportDoc.ParameterFields.Count) ? true : false;
        }

        /// <summary>
        /// Preenche as informações do relatório.
        /// </summary>
        /// <param name="filename">Nome/Caminho do arquivo .rpt do relatório.</param>
        /// <returns>Retorna verdadeiro se o relatório foi carregado corretamente.</returns>
        public bool ReportIsLoaded(string filename)
        {
            _reportDoc = SetReport(filename);
            return _reportDoc.IsLoaded;
        }

        #region -- Tipos de Formatos --
        /// <summary>
        /// Exibição no formato do tipo pdf.
        /// </summary>
        public void ExportToPDF()
        {
            ExportTo(ExportFormatType.PortableDocFormat, "application/pdf");
        }

        /// <summary>
        /// Exibição no formato do tipo excel.
        /// </summary>
        public void ExportToExcel()
        {
            ExportTo(ExportFormatType.Excel, "application/vnd.ms-excel");
        }

        /// <summary>
        /// Exibição no formato do tipo word.
        /// </summary>
        public void ExportToWord()
        {
            ExportTo(ExportFormatType.WordForWindows, "application/msword");
        }

        /// <summary>
        /// Exibição no formato do tipo html.
        /// </summary>
        public void ExportToHtml()
        {
            ExportTo(ExportFormatType.HTML40, "text/html");

        }

        /// <summary>
        /// Exibição no formato do tipo RTF.
        /// </summary>
        public void ExportToRTF()
        {
            ExportTo(ExportFormatType.EditableRTF, "application/rtf");
        }

        //public void SetRecordSelectionFormula(string selectionFormula)
        //{
        //    reportDoc.RecordSelectionFormula = selectionFormula;
        //}
        #endregion

        /// <summary>
        /// Libera o objeto.
        /// </summary>
        public void Dispose()
        {
            _reportDoc.Dispose();
        }
        #endregion

        #region -- Destruidor --
        /// <summary>
        /// Destruidor para finalização do código.
        /// </summary>
        ~CrystalReports()
        {
            _reportDoc.Close();
            Dispose();
        }
        #endregion

    }
}
